package com.clp.protocol.iec104.client;

import com.clp.protocol.iec104.client.config.MasterConfig;
import com.clp.protocol.iec104.client.config.MasterControlConfig;
import com.clp.protocol.iec104.client.config.MasterDataConfig;
import com.clp.protocol.iec104.client.pipeline.MPipelineManager;
import com.clp.protocol.iec104.client.pipeline.state.MInternalIAsduRecver;
import com.clp.protocol.iec104.client.pipeline.state.MInternalIAsduSender;
import com.clp.protocol.iec104.client.pipeline.state.control.MControlInfo;
import com.clp.protocol.iec104.client.pipeline.state.data.MDataInfo;
import com.clp.protocol.core.client.NettyClient;
import com.clp.protocol.core.event.EventListenerRegister;
import io.netty.channel.Channel;
import io.netty.channel.socket.SocketChannel;
import io.netty.util.concurrent.EventExecutor;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.net.InetSocketAddress;
import java.util.Objects;

@Slf4j
public class InMaster implements Master {
    @Getter
    private final NettyClient nettyClient;

    private final String remoteHost;
    private final int remotePort;
    private final int remoteRtuAddr;
    private volatile InetSocketAddress localAddress;
    private final int localRtuAddr; // 公共地址验证

    @Getter
    private final MasterControlConfig controlConfig;
    @Getter
    private final MasterDataConfig dataConfig;

    private volatile SocketChannel channel;
    private volatile MPipelineManager pipelineManager;
    @Getter
    private final InMasterConnector connector;
    @Getter
    private final MInternalIAsduRecver iAsduRecver;

    protected InMaster(NettyClient nettyClient, MasterConfig cfg) {
        this.nettyClient = nettyClient;
        // 设置通道、ip、端口号
        this.remoteHost = cfg.getRemoteHost();
        this.remotePort = cfg.getRemotePort();
        this.remoteRtuAddr = cfg.getRemoteRtuAddr();
        // 公共地址
        this.localRtuAddr = cfg.getLocalRtuAddr();
        this.controlConfig = cfg.getControlConfig();
        this.dataConfig = cfg.getDataConfig();

        this.connector = new InMasterConnector(this, cfg);
        this.iAsduRecver = new MInternalIAsduRecver(this);
    }

    void registerChannel(SocketChannel ch) {
        if (isConnected()) throw new IllegalStateException("原Channel未关闭！");
        this.channel = ch;
        this.pipelineManager = new MPipelineManager(this, ch.pipeline());
        this.localAddress = ch.localAddress();
    }

    public void resetChannel() {
        this.channel = null;
        this.pipelineManager = null;
        this.localAddress = null;
    }

    @Override
    public String remoteHost() {
        return remoteHost;
    }

    @Override
    public int remotePort() {
        return remotePort;
    }

    @Override
    public int remoteRtuAddr() {
        return remoteRtuAddr;
    }

    @Override
    public int localRtuAddr() {
        return localRtuAddr;
    }

    @Override
    public EventExecutor executor() {
        if (channel != null) return channel.eventLoop();
        return nettyClient.scheduledExecutorService();
    }

    public Channel channel() {
        if (!isConnected()) {
            throw new IllegalStateException("Channel is not activated");
        }
        return channel;
    }

    @Override
    public boolean isConnected() {
        return channel != null && channel.isOpen() && channel.isActive();
    }

    @Override
    public MControlInfo controlInfo() {
        return pipelineManager;
    }

    @Override
    public MDataInfo dataInfo() {
        return pipelineManager;
    }

    @Override
    public EventListenerRegister<Master> getEventListenerRegister() {
        return connector.getEventPublisher();
    }

    @Override
    public MInternalIAsduSender getIAsduSender() {
        return pipelineManager.getIAsduSender();
    }

    @Override
    public MInternalIAsduRecver getIAsduRecver() {
        return iAsduRecver;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        InMaster that = (InMaster) o;
        return remotePort == that.remotePort && Objects.equals(remoteHost, that.remoteHost);
    }

    @Override
    public int hashCode() {
        return Objects.hash(remoteHost, remotePort);
    }

    @Override
    public String toString() {
        return "Master{" +
                "remoteHost='" + remoteHost() + '\'' +
                ", remotePort=" + remotePort() +
                ", remoteRtuAddr=" + remoteRtuAddr() +
                '}';
    }
}
